package db

import (
	"context"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
	"time"
)

// Client represents a MongoDB client
var Client *mongo.Client

// Connect establishes a connection to the MongoDB database
func Connect(uri string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	clientOptions := options.Client().ApplyURI(uri)

	// Connect to MongoDB
	var err error
	Client, err = mongo.Connect(ctx, clientOptions)
	if err != nil {
		return err
	}

	// Check the connection
	err = Client.Ping(ctx, nil)
	if err != nil {
		return err
	}

	log.Println("Connected to MongoDB!")
	return nil
}

// GetCollection returns a reference to the specified collection
func getCollection(dbName string, collectionName string) *mongo.Collection {
	return Client.Database(dbName).Collection(collectionName)
}

// Close closes the MongoDB client connection
func Close() {
	if Client != nil {
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		if err := Client.Disconnect(ctx); err != nil {
			log.Printf("Error closing the MongoDB client: %v\n", err)
		}
	}
}

// FindOne finds a single document in a collection
func FindOne(collection *mongo.Collection, filter bson.D) (bson.D, error) {
	var result bson.D
	err := collection.FindOne(context.TODO(), filter).Decode(&result)
	return result, err
}

// InsertOne inserts a single document into a collection
func InsertOne(collection *mongo.Collection, document bson.D) (*mongo.InsertOneResult, error) {
	result, err := collection.InsertOne(context.TODO(), document)
	return result, err
}

// UpdateOne updates a single document in a collection
func UpdateOne(collection *mongo.Collection, filter bson.D, update bson.D) (*mongo.UpdateResult, error) {
	result, err := collection.UpdateOne(context.TODO(), filter, update)
	return result, err
}

// DeleteOne deletes a single document in a collection
func DeleteOne(collection *mongo.Collection, filter bson.D) (*mongo.DeleteResult, error) {
	result, err := collection.DeleteOne(context.TODO(), filter)
	return result, err
}

// InsertMany inserts multiple documents into a collection
func InsertMany(collection *mongo.Collection, documents []interface{}) (*mongo.InsertManyResult, error) {
	result, err := collection.InsertMany(context.TODO(), documents)
	return result, err
}

// UpdateMany updates multiple documents in a collection
func UpdateMany(collection *mongo.Collection, filter bson.D, update bson.D) (*mongo.UpdateResult, error) {
	result, err := collection.UpdateMany(context.TODO(), filter, update)
	return result, err
}

// DeleteMany deletes multiple documents in a collection
func DeleteMany(collection *mongo.Collection, filter bson.D) (*mongo.DeleteResult, error) {
	result, err := collection.DeleteMany(context.TODO(), filter)
	return result, err
}
